#include "pwext.hpp"
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <vector>

class pollable_event;
class master_event;

// [[ex07ba]]
// DEBUG descr The pollable event descriptor structure.
struct pollev {
	pollable_event *ev_;
	bool ready_;
};
// END

// [[ex07bb]]
// DEBUG descr The pollable event class.
class pollable_event
{
public:
	pollable_event(bool signaled = false) :
		signaled_(signaled),
		master_(0)
	{ }

	int wait()
	{
		pw::lockmutex lm(cond_);
		while (!signaled_) {
			cond_.wait();
		}
		return 0;
	}
	int trywait()
	{
		pw::lockmutex lm(cond_);
		if (!signaled_)
			return ETIMEDOUT;
		return 0;
	}
	int timedwait(const struct timespec &abstime)
	{
		pw::lockmutex lm(cond_);
		while (!signaled_) {
			if (cond_.timedwait(abstime) == ETIMEDOUT)
				return ETIMEDOUT;
		}
		return 0;
	}
	// DEBUG getfrom pollable_event::signal
	int signal();
	// DEBUG endget
	int reset()
	{
		pw::lockmutex lm(cond_);
		signaled_ = false;
		return 0;
	}

protected:
	// interface for the master_event
	friend class master_event;

	// DEBUG getfrom pollable_event::attach_master
	void attach_master(master_event *master, int id);
	// DEBUG endget
	void detach_master()
	{
		pw::lockmutex lm(cond_);
		master_ = 0;
	}

protected:
	// contains both condition variable and a mutex
	pw::pmcond cond_; 
	// event is in the signaled state
	bool signaled_; 
	// connection to the master event
	master_event *master_; 
	int id_in_master_;
};
// END

// [[ex07bc]]
// DEBUG descr The master event for the pollable events.
class master_event
{
public:
	typedef std::vector<pollev> pollvector;

	// returns 0 if signaled, ETIMEDOUT on timeout
	int poll(pollvector &events, timespec *timeout = 0)
	{
		int result;

		pollv_ = &events;
		ev_.reset();
		int n = (int) events.size();
		for (int i = 0; i < n; i++) {
			events[i].ready_ = false;
			events[i].ev_->attach_master(this, i);
		}
		if (timeout)
			result = ev_.timedwait(*timeout);
		else
			result = ev_.wait();
		for (int i = 0; i < n; i++) {
			events[i].ev_->detach_master();
		}
		return result;
	}

protected:
	// the pollable_event interface
	friend class pollable_event;

	void inform(int id)
	{
		(*pollv_)[id].ready_ = true;
		ev_.signal();
	}

protected:
	pw::event ev_; // will be used for waiting
	pollvector *pollv_;
};
// DEBUG move pollable_event::signal
	int /*DEBUG{*/pollable_event::/*}DEBUG*/signal()
	{
		pw::lockmutex lm(cond_);
		signaled_ = true;
		cond_.broadcast();
		if (master_)
			master_->inform(id_in_master_);
		return 0;
	}
// DEBUG endmove pollable_event::signal
// DEBUG move pollable_event::attach_master
	void /*DEBUG{*/pollable_event::/*}DEBUG*/attach_master(
		master_event *master, int id)
	{
		pw::lockmutex lm(cond_);
		if (master_ && master)
			abort();
		master_ = master;
		id_in_master_ = id;
		if (signaled_ && master_)
			master_->inform(id_in_master_);
	}
// DEBUG endmove pollable_event::attach_master
// END

// DEBUG {

const int NEVS = 20;
pollable_event evs[NEVS];
master_event::pollvector polls(NEVS);
master_event mev;
pw::event ready;

class detector : public pw::pwthread
{
public:
	void *execute()
	{
		ready.signal();
		while(true) {
			int res = mev.poll(polls);
			if (res != 0) {
				fprintf(stderr, "Master error %d\n", errno);
				return 0;
			}
			fprintf(stderr, "---\n");
			for (int i = 0; i < NEVS; i++)
				if (polls[i].ready_) {
					fprintf(stderr, "%d ", i);
					evs[i].reset();
				}
			fprintf(stderr, "\n");
			if (polls[NEVS-1].ready_)
				return 0;
		}
	}
};

int main()
{
	for (int i = 0; i < NEVS; i++)
		polls[i].ev_ = &evs[i];
	detector det;
	det.start();
	ready.wait();
	for (int i = 0; i < NEVS; i++) {
		evs[i].signal();
		usleep(1);
	}
	det.join();
	return 0;
}
// DEBUG }
/* Sample output:
---
0 
---
1 2 3 4 5 6 7 
---
8 9 10 11 12 13 14 15 16 17 18 19 
*/
